﻿using System;
using System.Collections;
using UnityEngine;


public class WallRunning : MonoBehaviour
{
    [Header("Wall Running")]
    public LayerMask whatIsWall;
    public LayerMask whatIsGround;
    public float wallRunForce;
    public float maxWallRunTime;
    public float wallRunTimer;
    public float wallClimbSpeed;
    public float wallJumpUpForce;
    public float wallJumpSlideForce;

    [Header("Gravity")]
    public bool useGravity;
    public float gravityCounterForce;

    [Header("Detection")]
    public float wallCheckDistance;
    public float minJumpHeight;
    private RaycastHit leftWallHit;
    private RaycastHit rightWallHit;
    public bool wallLeft;
    public bool wallRight;

    [Header("References")]
    public Transform orientation;
    public PlayerMovement playerMovement;
    private Rigidbody rb;
    public Vector2 inputDirection = Vector2.zero;
    public bool wallUpWardsRun = false;
    public bool wallDownWardsRun = false;
    public bool canJump = false;
    public LedgeGrabbing ledgeGrabbing;
    [Header("Exiting")]
    public bool exitingWall;
    public float exitWallTime;
    public float exitWallTimer;

    [Header("Debug")]
    public Vector3 debugVelocity;
    private void Start()
    {
        rb = GetComponent<Rigidbody>();
        playerMovement = GetComponent<PlayerMovement>();
        playerMovement.OnUpWardsRun += UpWardsRun;
        playerMovement.OnDownWardsRun += DownWardsRun;
        playerMovement.OnJumpAction += JumpHandler;
    }

    private void JumpHandler(bool obj)
    {
        canJump = obj;
    }

    private void DownWardsRun(bool obj)
    {
        wallUpWardsRun = obj;
    }

    private void UpWardsRun(bool obj)
    {
        wallUpWardsRun = obj;
    }
    private void OnApplicationQuit()
    {
        playerMovement.OnUpWardsRun -= UpWardsRun;
        playerMovement.OnDownWardsRun -= DownWardsRun;
    }
    private void Update()
    {
        CheckForWall();
        StateMachine();
        debugVelocity = rb.velocity;
    }
    private void FixedUpdate()
    {
        if(playerMovement.wallRunning)
            WallRunMovement();

    }
    private void CheckForWall()
    {
        wallRight = Physics.Raycast(transform.position, orientation.right, out rightWallHit, wallCheckDistance, whatIsWall);
        wallLeft = Physics.Raycast(transform.position, -orientation.right, out leftWallHit, wallCheckDistance, whatIsWall);
    }

    private bool AboveGround()
    {
        return !Physics.Raycast(transform.position, Vector3.down, minJumpHeight, whatIsGround);
    }
    private void StateMachine()
    {


        inputDirection = playerMovement.inputDirection;
        if((wallLeft || wallRight) && inputDirection.y > 0f && AboveGround()&& !exitingWall)
        {
            if (!playerMovement.wallRunning)
            {
                StartWallRun();
            }
            if (wallRunTimer > 0)
            {
                wallRunTimer -= Time.deltaTime;
            }
            if(wallRunTimer <= 0 && playerMovement.wallRunning)
            {
                exitingWall = true;
                exitWallTimer = exitWallTime;
            }
            if (canJump)
            {
                WallJump();
            }
        }
        else if (exitingWall)
        {
            if (playerMovement.wallRunning)
            {
                StopWallRun();
            }
            if(exitWallTimer > 0)
            {
                exitWallTimer -= Time.deltaTime;
            }
            if(exitWallTimer <= 0)
            {
                exitingWall = false;
            }
        }
        else
        {
            if (playerMovement.wallRunning)
            {
                StopWallRun();
            }
        }
    }
    private void StartWallRun()
    {
        wallRunTimer = maxWallRunTime;
        playerMovement.wallRunning = true;
        rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
        playerMovement.DoFov(90f);
        if (wallLeft)
        {
            playerMovement.DoTilt(-5f);
        }
        else
        {
            playerMovement.DoTilt(5f);
        }
    }
    private void WallRunMovement()
    {
        rb.useGravity = useGravity;
        Vector3 wallNormal = wallRight?rightWallHit.normal:leftWallHit.normal;
        Vector3 wallForward = Vector3.Cross(wallNormal, transform.up);
        if((orientation.forward - wallForward).magnitude>(orientation.forward - -wallForward).magnitude)
        {
            wallForward = -wallForward;
        }

        if (wallUpWardsRun)
        {
            rb.velocity = new Vector3(rb.velocity.x, wallClimbSpeed, rb.velocity.z);
        }
        if (wallDownWardsRun)
        {
            rb.velocity = new Vector3(rb.velocity.x, -wallClimbSpeed, rb.velocity.z);
        }

        Debug.DrawLine(transform.position, transform.position + wallForward * 10, Color.red);
        rb.AddForce(wallForward * wallRunForce, ForceMode.Force);
        if (!(wallLeft && inputDirection.x > 0) && !(wallRight && inputDirection.x < 0))
        {
            rb.AddForce(-wallNormal * 100, ForceMode.Force);
        }
        if (useGravity)
        {
            rb.AddForce(transform.up * gravityCounterForce, ForceMode.Force);
        }
    }
    private void StopWallRun()
    {
        playerMovement.wallRunning = false;

        playerMovement.DoFov(80f);
        playerMovement.DoTilt(0f);
    }
    private void WallJump()
    {
        if (ledgeGrabbing.playerMovement || ledgeGrabbing.exitingLedge) return;


        exitingWall = true;
        exitWallTimer = exitWallTime;
        Vector3 wallNormal = wallRight?rightWallHit.normal:leftWallHit.normal;
        Vector3 forceToApply = transform.up * wallJumpUpForce + wallNormal * wallJumpSlideForce;

        rb.velocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
        rb.AddForce(forceToApply, ForceMode.Impulse);
    }
}
